
package com.example.caorl.job1.conc0302.lock;

import java.util.concurrent.Semaphore;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class ObjectCache<T> {

    public interface ObjectFactory<T> {
        T makeObject();
    }

    class Node {
        T obj;
        Node next; // 单向链表
    }

    final int capacity; //容量
    final ObjectFactory<T> factory;
    final Lock lock = new ReentrantLock();
    final Semaphore semaphore;
    private Node head;
    private Node tail;

    public ObjectCache(int capacity, ObjectFactory<T> factory) {
        this.capacity = capacity;
        this.factory = factory;
        this.semaphore = new Semaphore(this.capacity); // 控制允许多少个线程同时访问
        this.head = null;
        this.tail = null;
    }

    public T getObject() throws InterruptedException {
        semaphore.acquire(); //从此信号量获取一个许可，在提供一个许可前一直将线程阻塞，否则线程被中断
        return getNextObject();
    }

    private T getNextObject() {
        lock.lock();
        try {
            if (head == null) {
                return factory.makeObject(); //头部为空时生成一个对象
            } else {
                Node ret = head;
                head = head.next;
                if (head == null) tail = null;
                ret.next = null;//help GC
                return ret.obj;
            }
        } finally {
            lock.unlock();
        }
    }

    // 把对象放进缓存池
    private void returnObjectToPool(T t) {
        lock.lock(); //并发写锁，只允许一个线程写
        try {
            Node node = new Node();
            node.obj = t;
            if (tail == null) {
                head = tail = node; //第一次存一个元素，头部等于尾部
            } else {
                tail.next = node; //新增的node指到尾部的下一个元素，即尾部插入新元素
                tail = node;
            }

        } finally {
            lock.unlock();
        }
    }

    //把对象放进缓存池
    public void returnObject(T t) {
        // 加入
        returnObjectToPool(t);
        semaphore.release();
    }

    public static void main(String[] args) {
        ObjectFactory<Integer> objectFactory = new ObjectFactory<Integer>() {
            @Override
            public Integer makeObject() {
                /*int max = 10000;
                int min = 1;
                return (int) (int) (Math.random() * (max - min) + min);*/
                return null;
            }
        };
        ObjectCache<Integer> integerObjectCache = new ObjectCache<>(10, objectFactory);
        for (int i = 0;i<100;i++){
            integerObjectCache.returnObject(i);
        }
        for (int i = 0;i<1000;i++) {
            System.out.println(integerObjectCache.getNextObject());
        }
    }
}
